package com.xbongbong.saas.help;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.i18n.util.I18nMessageUtil;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.constant.TimeConstant;
import com.xbongbong.paas.domain.entity.PaasAppEntity;
import com.xbongbong.paas.domain.entity.PaasFormDataEntity;
import com.xbongbong.paas.domain.entity.PaasProcessRelatedDataEntity;
import com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt;
import com.xbongbong.paas.elasticsearch.util.EsHelper;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.FlowStatusEnum;
import com.xbongbong.paas.enums.IndexTypeEnum;
import com.xbongbong.paas.help.CommonHelp;
import com.xbongbong.paas.help.FormDataHelp;
import com.xbongbong.paas.help.subform.TransferSubFormHelper;
import com.xbongbong.paas.model.PaasAppModel;
import com.xbongbong.paas.model.PaasProcessRelatedDataModel;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.Arith;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.DateTimeUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.pro.constant.BusinessConstant;
import com.xbongbong.pro.constant.CommonConstant;
import com.xbongbong.pro.constant.I18nStringConstant;
import com.xbongbong.pro.contract.pojo.vo.ContractOutstockDeleteVO;
import com.xbongbong.pro.contract.pojo.vo.ContractProductUpdateOutstockNumVO;
import com.xbongbong.pro.enums.errorcodes.OutstockErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.ProductErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.product.pojo.ProductSavePojo;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.ParameterConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.domain.entity.BusinessProductEntity;
import com.xbongbong.saas.domain.entity.ContractProductEntity;
import com.xbongbong.saas.domain.entity.InstockProductEntity;
import com.xbongbong.saas.domain.entity.OutstockProductEntity;
import com.xbongbong.saas.domain.entity.ProductStockEntity;
import com.xbongbong.saas.domain.entity.ProductWarehouseEntity;
import com.xbongbong.saas.domain.entity.SpecificationEntity;
import com.xbongbong.saas.domain.entity.ext.ContractEntityExt;
import com.xbongbong.saas.domain.entity.ext.OutstockEntityExt;
import com.xbongbong.saas.domain.entity.ext.ProductEntityExt;
import com.xbongbong.saas.domain.entity.ext.RefundEntityExt;
import com.xbongbong.saas.enums.ContractProductTypeEnum;
import com.xbongbong.saas.enums.InstockTypeEnum;
import com.xbongbong.saas.enums.OutstockTypeEnum;
import com.xbongbong.saas.enums.SelectProductEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.ContractEnum;
import com.xbongbong.saas.enums.business.ProductEnum;
import com.xbongbong.saas.enums.business.RefundEnum;
import com.xbongbong.saas.enums.business.ReturnedPurchaseEnum;
import com.xbongbong.saas.enums.subform.ProductStockSubFormEnum;
import com.xbongbong.saas.enums.subform.ProductWarehouseSubFormEnum;
import com.xbongbong.saas.model.ContractProductModel;
import com.xbongbong.saas.model.InstockModel;
import com.xbongbong.saas.model.InstockProductModel;
import com.xbongbong.saas.model.OutstockModel;
import com.xbongbong.saas.model.OutstockProductModel;
import com.xbongbong.saas.model.ProductModel;
import com.xbongbong.saas.model.RefundModel;
import com.xbongbong.saas.model.ReturnedPurchaseProductModel;
import com.xbongbong.saas.model.SpecificationModel;
import com.xbongbong.saas.toolbox.help.FastJsonHelper;
import com.xbongbong.util.ProductUtil;
import com.xbongbong.workflow.domain.entity.WorkflowTaskEntity;
import com.xbongbong.workflow.model.WorkflowTaskModel;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static com.xbongbong.paas.elasticsearch.constant.EsNormalConstant.MINI_MUM_SHOULD_MATCH;
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;


/**
 * @author 吴峰
 * @version v1.0
 * @date 2019/4/5 16:12
 * @since v1.0
 */
@Component
public class SaasProductHelp {
    @Resource
    private OutstockProductModel outstockProductModel;
    @Resource
    private InstockProductModel instockProductModel;
    @Resource
    private SpecificationModel specificationModel;
    @Resource
    private EsHelper esHelper;
    @Resource
    private FormDataHelp formDataHelp;
    @Resource
    private PaasProcessRelatedDataModel paasProcessRelatedDataModel;
    @Resource
    private ProductModel productModel;
    @Resource
    private TransferSubFormHelper transferSubFormHelper;
    @Resource
    private RelativeProductHelp relativeProductHelp;
    @Resource
    private RefundModel refundModel;
    @Resource
    private ContractProductModel contractProductModel;
    @Resource
    private ReturnedPurchaseProductModel returnedPurchaseProductModel;
    @Resource
    private PaasAppModel paasAppModel;
    @Resource
    private CommonHelp commonHelp;
    @Resource
    private OutstockModel outstockModel;
    @Resource
    private InstockModel instockModel;
    @Resource
    private WorkflowTaskModel workflowTaskModel;

    /**
     * 获取未入库产品map
     * @param corpid 公司id
     * @param refId 关联单据id
     * @param type 关联单据类型
     * @return 剩余未入库产品
     * @throws XbbException
     */
    public Map<String,Double> getUnInstockProduct(String corpid, Long refId, Integer type, boolean needMerge, Long taskId, Long dataId) throws XbbException {
        //  未开启工作流需要走之前的校验方法；
        boolean noOpenWorkflow = !commonHelp.isOpenWorkFlow(corpid);
        List<InstockProductEntity> instockProductEntityList;
        if (noOpenWorkflow) {
            instockProductEntityList = instockProductModel.getUpStreamProduct(corpid,type,refId);
        } else {
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("corpid", corpid);
            param.put("type", type);
            param.put("refId", refId);
            param.put("del", 0);
            param.put("negIntoWarehouseId", dataId);
            param.put("orderByStr", "sort");
            instockProductEntityList =  instockProductModel.findEntitys(param);
        }
        InstockTypeEnum instockTypeEnum = InstockTypeEnum.getByCode(type);
        boolean oldApprove = noOpenWorkflow && (Objects.equals(instockTypeEnum, InstockTypeEnum.PURCHASE_INSTOCK) || Objects.equals(instockTypeEnum, InstockTypeEnum.TRANSFER_INSTOCK));
        if (oldApprove) {
            // 老审批增加判断
            List<PaasProcessRelatedDataEntity> processDataList = paasProcessRelatedDataModel.getByRefIdAndBusinessType(refId, instockTypeEnum.getStockBusinessType(), corpid);
            Map<String, Double> processDataMap = new HashMap<>(processDataList.size());
            processDataList.forEach(item -> {
                if (!Objects.equals(item.getTaskId(), taskId)) {
                    String key = item.getFourthRefId() + "_" + item.getSecondRefId();
                    Double num = processDataMap.getOrDefault(key, 0D);
                    num = Arith.add(num, item.getValue());
                    processDataMap.put(key, num);
                }
            });
            instockProductEntityList.forEach(item -> {
                String key = item.getProductId() + "_" + item.getRefProductId();
                if (processDataMap.containsKey(key)) {
                    item.setProductNum(Arith.add(processDataMap.get(key), item.getProductNum()));
                    processDataMap.remove(key);
                }
            });
            // 只有审批中有的时候需要加到入库产品中
            for (Map.Entry<String, Double> entry : processDataMap.entrySet()) {
                String key = entry.getKey();
                Double value = entry.getValue();
                InstockProductEntity instockProductEntity = new InstockProductEntity();
                String[] args = key.split("_");
                instockProductEntity.setProductId(Long.valueOf(args[0]));
                instockProductEntity.setRefProductId(Long.valueOf(args[1]));
                instockProductEntity.setProductNum(value);
                instockProductEntityList.add(instockProductEntity);
            }
        }
        List<BusinessProductEntity> businessProductEntityList = relativeProductHelp.getInstockRelyBusinessProduct(corpid,refId,type, needMerge);
        return ProductUtil.subtractInstock(instockProductEntityList, businessProductEntityList, needMerge);
    }

    /**
     * 入库单获取退货退款未入库(特殊)
     * @param corpid
     * @param refId
     * @param type
     * @return
     */
    public Map<String,Double> getRefundUnInstockProduct(String corpid, Long refId, Integer type, boolean needMerge) throws XbbException {
        List<InstockProductEntity> instockProductEntityList = instockProductModel.getUpStreamProduct(corpid,type,refId);
        RefundEntityExt refundEntityExt = refundModel.getByKey(refId,corpid);
        Long redContractId = refundEntityExt.getData().getLong(RefundEnum.RED_CONTRACT_ID.getAttr());
        Map<String,Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid",corpid);
        param.put("contractId",redContractId);
        param.put("type", XbbRefTypeEnum.REFUND.getCode());
        param.put("del",0);
        List<BusinessProductEntity> businessProductEntityList = contractProductModel.findProductNum(param);
        return ProductUtil.subtractInstock(instockProductEntityList,businessProductEntityList,needMerge);
    }


    /**
     * 获取未出库产品map
     * @param corpid 公司id
     * @param refId 关联单据id
     * @param type 关联单据类型
     * @return 剩余未出库产品
     * @throws XbbException
     */
    public Map<String,Double> getUnOutstockProduct(String corpid, Long refId, Integer type, boolean needMerge, Long taskId, Long dataId) throws XbbException {
        // 未开启工作流需要走之前的校验方法
        boolean noOpenWorkflow = !commonHelp.isOpenWorkFlow(corpid);
        List<OutstockProductEntity> outstockProductEntityList;
        if (noOpenWorkflow) {
            outstockProductEntityList = outstockProductModel.getUpStreamProduct(corpid,type,refId);
        } else {
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("corpid", corpid);
            param.put("refId", refId);
            param.put("type", type);
            param.put("del", 0);
            param.put("negOutWarehouseId", dataId);
            param.put(ParameterConstant.ORDER_BY_STR, BasicConstant.SORT);
            outstockProductEntityList = outstockProductModel.findEntitys(param);
        }
        OutstockTypeEnum outstockTypeEnum = OutstockTypeEnum.getByCode(type);
        boolean oldApprove = noOpenWorkflow && (Objects.equals(outstockTypeEnum, OutstockTypeEnum.CONTRACT_OUTSTOCK) || Objects.equals(outstockTypeEnum, OutstockTypeEnum.WORK_ORDER_OUTSTOCK));
        if (oldApprove) {
            List<PaasProcessRelatedDataEntity> processDataList = paasProcessRelatedDataModel.getByRefIdAndBusinessType(refId, outstockTypeEnum.getStockBusinessType(), corpid);
            Map<String, Double> processDataMap = new HashMap<>(processDataList.size());
            processDataList.forEach(item -> {
                if (!Objects.equals(item.getTaskId(), taskId)) {
                    String key = item.getFourthRefId() + "_" + item.getSecondRefId();
                    Double num = processDataMap.getOrDefault(key, 0D);
                    num = Arith.add(num, item.getValue());
                    processDataMap.put(key, num);
                }
            });
            outstockProductEntityList.forEach(item -> {
                String key = item.getProductId() + "_" + item.getRefProductId();
                if (processDataMap.containsKey(key)) {
                    item.setProductNum(Arith.add(processDataMap.get(key), item.getProductNum()));
                    processDataMap.remove(key);
                }
            });
            // 只有审批中有的时候需要加到入库产品中
            processDataMap.forEach((key, value) -> {
                OutstockProductEntity outstockProductEntity = new OutstockProductEntity();
                String[] args = key.split("_");
                outstockProductEntity.setProductId(Long.valueOf(args[0]));
                outstockProductEntity.setRefProductId(Long.valueOf(args[1]));
                outstockProductEntity.setProductNum(value);
                outstockProductEntityList.add(outstockProductEntity);
            });
        }
        List<BusinessProductEntity> businessProductEntityList = relativeProductHelp.getOutstockRelyBusinessProduct(corpid,refId,type,needMerge);
        return ProductUtil.subtractOutstock(outstockProductEntityList,businessProductEntityList,needMerge);
    }



    /**
     * 获取出库数量（包含审批中数据）
     * @param corpid 公司id
     * @param refId 关联业务id
     * @param type 关联业务类型
     * @throws XbbException
     * @return 已出库数量
     */
    public Map<String,Double> getOutstockProduct(String corpid, Long refId,Integer type, Map<Long,String> pageProductMap) throws XbbException {
        //  未开启工作流需要走之前的校验方法；
        Map<String,Double> businessProductMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        boolean noOpenWorkflow = !commonHelp.isOpenWorkFlow(corpid);
        Map<String,Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        if (!noOpenWorkflow){
            param.put("corpid", corpid);
            param.put("del", 0);
            param.put("typeScript",OutstockTypeEnum.CONTRACT_OUTSTOCK.getCode());
            param.put("refId",refId);
            param.put("flowStatusIn",FlowStatusEnum.inApproval());
            List<OutstockEntityExt> outstockEntityExts = outstockModel.findEntitysWithoutSub(param);
            if (CollectionsUtil.isNotEmpty(outstockEntityExts)) {
                businessProductMap.put(BusinessConstant.HAS_APPROVAL_DATA, null);
                return businessProductMap;
            }
        }
        // 已出库产品
        param.clear();
        param.put("corpid", corpid);
        param.put("del", 0);
        param.put("refId",refId);
        param.put("type",type);
        List<BusinessProductEntity> outstockProductEntityList = outstockProductModel.findProductNum(param);
        boolean needMerge = false;
        for (BusinessProductEntity productEntity : outstockProductEntityList) {
            if (Objects.isNull(productEntity.getRefProductId())) {
                needMerge = true;
            }
            if (!pageProductMap.containsKey(productEntity.getProductId())) {
                pageProductMap.put(productEntity.getProductId(), productEntity.getProductName());
            }
        }
        OutstockTypeEnum outstockTypeEnum = OutstockTypeEnum.getByCode(type);
        boolean oldApprove = noOpenWorkflow && (Objects.equals(outstockTypeEnum, OutstockTypeEnum.CONTRACT_OUTSTOCK) || Objects.equals(outstockTypeEnum, OutstockTypeEnum.WORK_ORDER_OUTSTOCK));
        if (oldApprove) {
            List<PaasProcessRelatedDataEntity> processDataList = paasProcessRelatedDataModel.getByRefIdAndBusinessType(refId, outstockTypeEnum.getStockBusinessType(), corpid);
            // 有审批中的数据标识 fix 32198
            if (!processDataList.isEmpty()) {
                businessProductMap.put(BusinessConstant.HAS_APPROVAL_DATA, null);
            }
            Map<String, Double> processDataMap = new HashMap<>(processDataList.size());
            processDataList.forEach(item -> {
                String key = item.getFourthRefId() + "_" + item.getSecondRefId();
                Double num = processDataMap.getOrDefault(key, 0D);
                num = Arith.add(num, item.getValue());
                processDataMap.put(key, num);
            });
            outstockProductEntityList.forEach(item -> {
                String key = item.getProductId() + "_" + item.getRefProductId();
                if (processDataMap.containsKey(key)) {
                    item.setProductNum(Arith.add(processDataMap.get(key), item.getProductNum()));
                    processDataMap.remove(key);
                }
            });
            // 只有审批中有的时候需要加到出库产品中
            processDataMap.forEach((key, value) -> {
                BusinessProductEntity outstockProductEntity = new BusinessProductEntity();
                String[] args = key.split("_");
                outstockProductEntity.setProductId(Long.valueOf(args[0]));
                try {
                    outstockProductEntity.setRefProductId(Long.valueOf(args[1]));
                } catch (Exception e) {

                }
                outstockProductEntity.setProductNum(value);
                outstockProductEntityList.add(outstockProductEntity);
            });
        }
        // 合同删掉产品，审批中有产品，抛异常的时候无法拿到名称 30772
        List<Long> noExistInPageProduct = new ArrayList<>();
        for (BusinessProductEntity entity : outstockProductEntityList){
            if (!pageProductMap.containsKey(entity.getProductId())) {
                noExistInPageProduct.add(entity.getProductId());
            }
            String key;
            if (needMerge) {
                key = entity.getProductId().toString();
            } else {
                key = entity.getProductId() + "_" + entity.getRefProductId();
            }
            Double num  = businessProductMap.getOrDefault(key,0D);
            num = Arith.add(num, entity.getProductNum());
            businessProductMap.put(key,num);
        }
        if (!noExistInPageProduct.isEmpty()) {
            List<ProductEntityExt> productEntityExtList = productModel.getProductListByIdIn(corpid, noExistInPageProduct, DelEnum.NORMAL.getDel());
            productEntityExtList.forEach(item -> pageProductMap.put(item.getId(), item.getData().getString(ProductEnum.NAME.getAttr())));
        }
        return businessProductMap;
    }

    /**
     * 获取入库单审批中的产品数量
     * @param corpid 公司id
     * @param refId 关联单据id
     * @param type 类型
     * @param businessType 关联业务类型
     * @param taskId 流程任务id
     * @param noOpenWorkflow 是否开启工作流 true：未开启工作流， false：已开启工作流
     * @return
     * @throws XbbException
     */
    private Map<String, Double> getInstockProcessDataMap(String corpid, Long refId, Integer type, Integer businessType, Long taskId, boolean noOpenWorkflow) throws XbbException {
        Map<String, Double> processDataMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        //  未开启工作流需要走之前的校验方法；
        if (noOpenWorkflow) {
            List<PaasProcessRelatedDataEntity> processDataList = paasProcessRelatedDataModel.getByRefIdAndBusinessType(refId, businessType, corpid);
            processDataList.forEach(item -> {
                if (!Objects.equals(item.getTaskId(), taskId)) {
                    String key = item.getFourthRefId() + "_" + item.getSecondRefId();
                    Double num = processDataMap.getOrDefault(key, 0D);
                    num = Arith.add(num, item.getValue());
                    processDataMap.put(key, num);
                }
            });
        } else {
            // 查询审批中的入库单产品
            List<PaasFormDataEntityExt> approvalPaasFormDataList = instockModel.findInstockList(corpid, type, refId,
                    Arrays.asList(FlowStatusEnum.APPROVAL_PENDING.getType(), FlowStatusEnum.IN_APPROVAL.getType()), Arrays.asList(FieldTypeEnum.DATAID.getAlias()));
            if (CollectionsUtil.isNotEmpty(approvalPaasFormDataList)) {
                List<Long> instockIdApproveList = new ArrayList<>();
                approvalPaasFormDataList.forEach(item->instockIdApproveList.add(item.getDataId()));
                if (Objects.nonNull(taskId)) {
                    WorkflowTaskEntity workflowTaskEntity = workflowTaskModel.getByKey(taskId, corpid);
                    // 去掉自己
                    instockIdApproveList.remove(workflowTaskEntity.getDataId());
                }
                List<InstockProductEntity> instockProductList = instockProductModel.getProductsByInstockIdAndType(corpid, instockIdApproveList, type);
                instockProductList.forEach(item -> {
                    String key = item.getProductId() + "_" + item.getRefProductId();
                    Double num = processDataMap.getOrDefault(key, 0D);
                    num = Arith.add(num, item.getProductNum());
                    processDataMap.put(key, num);
                });
            }
        }
        return processDataMap;
    }

    /**
     * 获取出库单审批中的产品数量
     * @param corpid 公司id
     * @param refId 关联单据id
     * @param type 类型
     * @param businessType 关联业务类型
     * @param taskId 流程任务id
     * @param noOpenWorkflow 是否开启工作流 true：未开启工作流， false：已开启工作流
     * @return
     * @throws XbbException
     */
    private Map<String, Double> getOutstockProcessProductMap(String corpid, Long refId, Integer type, Integer businessType, Long taskId, boolean noOpenWorkflow) throws XbbException {
        Map<String, Double> processDataMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);

        if (noOpenWorkflow) {
            List<PaasProcessRelatedDataEntity> processDataList = paasProcessRelatedDataModel.getByRefIdAndBusinessType(refId, businessType, corpid);
            processDataList.forEach(item -> {
                if (!Objects.equals(item.getTaskId(), taskId)) {
                    String key = item.getFourthRefId() + "_" + item.getSecondRefId();
                    Double num = processDataMap.getOrDefault(key, 0D);
                    num = Arith.add(num, item.getValue());
                    processDataMap.put(key, num);
                }
            });
        } else {
            List<PaasFormDataEntityExt> approvalPaasFormDataList = outstockModel.findOutstockList(corpid, type, refId,
                    Arrays.asList(FlowStatusEnum.APPROVAL_PENDING.getType(), FlowStatusEnum.IN_APPROVAL.getType()), Arrays.asList(FieldTypeEnum.DATAID.getAlias()));
            if (CollectionsUtil.isNotEmpty(approvalPaasFormDataList)) {
                List<Long> outstockIdApproveList = new ArrayList<>();
                approvalPaasFormDataList.forEach(item->outstockIdApproveList.add(item.getDataId()));
                if (Objects.nonNull(taskId)) {
                    WorkflowTaskEntity workflowTaskEntity = workflowTaskModel.getByKey(taskId, corpid);
                    // 去掉自己
                    outstockIdApproveList.remove(workflowTaskEntity.getDataId());
                }
                List<OutstockProductEntity> outstockProductList = outstockProductModel.getProductsByOutstockIdAndType(corpid, outstockIdApproveList, type);
                outstockProductList.forEach(item -> {
                    String key = item.getProductId() + "_" + item.getRefProductId();
                    Double num = processDataMap.getOrDefault(key, 0D);
                    num = Arith.add(num, item.getProductNum());
                    processDataMap.put(key, num);
                });
            }
        }
        return processDataMap;
    }
    /**
     * 产品出库仓库库存校验
     * @param corpid 公司id
     * @param productArray 产品
     * @param pageWarehouseId 如果productArray中没有仓库信息，则使用传入值
     * @param warehouseFromOut 仓库是否来自于产品外
     * @param outstockType
     * @throws XbbException 库存不存在或库存不足校验
     */
    public void warehouseProductNumLegalCheck(String corpid, JSONArray productArray, Long pageWarehouseId, boolean warehouseFromOut, Integer outstockType) throws XbbException {
        BoolQueryBuilder warehouseBuilder = boolQuery();
        BoolQueryBuilder batchBuilder = boolQuery();
        boolean warehouseSearch = false;
        boolean batchSearch = false;
        Map<Long,String> warehouseNameMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        List<Long> productIdList = new ArrayList<>();
        boolean replaceCost = !Objects.equals(outstockType, OutstockTypeEnum.RETURNED_PURCHASE_OUTSTOCK.getCode()) && !Objects.equals(outstockType, OutstockTypeEnum.TRANSFER_OUTSTOCK.getCode());
        for (int i = 0; i < productArray.size(); i++) {
            JSONObject productObj = productArray.getJSONObject(i);
            Long productId = productObj.getLong(SelectProductEnum.PRODUCT.getAttr());
            productIdList.add(productId);
            Long warehouseId;
            if (warehouseFromOut){
                warehouseId = pageWarehouseId;
            }else {
                SaveFormatHelp.formatLinkBusiness4Save(productObj,SelectProductEnum.WAREHOUSE.getAttr(),"warehouseName",FieldTypeEnum.LINK_BUSINESS_SINGLE.getType());
                warehouseId = productObj.getLong(SelectProductEnum.WAREHOUSE.getAttr());
            }
            //String batch = productObj.getString(SelectProductEnum.BATCH.getAttr());
            //if (Objects.isNull(batch) || batch.isEmpty()){
            BoolQueryBuilder shouldBuilder = new BoolQueryBuilder();
            shouldBuilder.filter(termQuery(ProductWarehouseSubFormEnum.getEsAttr(ProductWarehouseSubFormEnum.PRODUCT_ID),productId));
            shouldBuilder.filter(termQuery(ProductWarehouseSubFormEnum.getEsAttr(ProductWarehouseSubFormEnum.WAREHOUSE_ID),warehouseId));
            warehouseBuilder.should(shouldBuilder);
            warehouseSearch = true;

            BoolQueryBuilder batchShould = new BoolQueryBuilder();
            batchShould.filter(termQuery(ProductStockSubFormEnum.getEsAttr(ProductStockSubFormEnum.PRODUCT_ID),productId));
            batchShould.filter(termQuery(ProductStockSubFormEnum.getEsAttr(ProductStockSubFormEnum.WAREHOUSE_ID),warehouseId));
            batchBuilder.should(batchShould);
            batchSearch = true;
            /*} else {
                BoolQueryBuilder shouldBuilder = new BoolQueryBuilder();
                shouldBuilder.filter(termQuery("productId",productId));
                shouldBuilder.filter(termQuery("warehouseId",warehouseId));
                shouldBuilder.filter(termQuery("batch.keyword",batch));
                batchBuilder.should(shouldBuilder);
                batchSearch = true;
            }*/
        }
        List<ProductEntityExt> productList = productModel.getProductListByIdIn(corpid, productIdList, 0);
        Map<Long, ProductEntityExt> productMap = new HashMap<>(productList.size());
        productList.forEach(item -> productMap.put(item.getId(), item));
        Map<String,Double> warehouseNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<String,Double> warehouseCostMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<String,Double> batchNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<String,Double> batchCostMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        //禁止库存不足允许出库
        if (warehouseSearch) {
            //仓库库存
            warehouseBuilder.minimumShouldMatch(MINI_MUM_SHOULD_MATCH);
            warehouseBuilder.filter(termQuery("corpid.keyword", corpid));
            warehouseBuilder.filter(termQuery(StringConstant.DEL, DelEnum.NORMAL.getDel()));
            warehouseBuilder.filter(termQuery(ProductWarehouseSubFormEnum.getEsAttr(ProductWarehouseSubFormEnum.WAREHOUSE_CHECKED), 1));
            List<PaasFormDataEntity> productWarehouseEntityList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_PRODUCT_WAREHOUSE, warehouseBuilder, PaasFormDataEntity.class, Arrays.asList(ProductWarehouseSubFormEnum.getAttrConnectData(ProductWarehouseSubFormEnum.PRODUCT_ID), ProductWarehouseSubFormEnum.getAttrConnectData(ProductWarehouseSubFormEnum.WAREHOUSE_ID), ProductWarehouseSubFormEnum.getAttrConnectData(ProductWarehouseSubFormEnum.WAREHOUSE_NAME), ProductWarehouseSubFormEnum.getAttrConnectData(ProductWarehouseSubFormEnum.NUM),ProductWarehouseSubFormEnum.getAttrConnectData(ProductWarehouseSubFormEnum.COST)));
            List<ProductWarehouseEntity> productWarehouseEntities = transferSubFormHelper.transferFormDataToProductWarehouseList(productWarehouseEntityList);
            for (ProductWarehouseEntity entity : productWarehouseEntities) {
                String warehouseKey = entity.getProductId() + "_" + entity.getWarehouseId();
                Double num = warehouseNumMap.getOrDefault(warehouseKey, 0D);
                num = Arith.add(num, entity.getNum());
                warehouseNumMap.put(warehouseKey, num);
                warehouseCostMap.put(warehouseKey, Objects.isNull(entity.getCost()) ? 0D : entity.getCost());
                warehouseNameMap.put(entity.getWarehouseId(),entity.getWarehouseName());
            }
        }
        // 产品仓库的所有批次总库存
        Map<String,Double> productWarehouseBatchNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        if (batchSearch) {
            //批次库存
            batchBuilder.minimumShouldMatch(MINI_MUM_SHOULD_MATCH);
            batchBuilder.filter(termQuery("corpid.keyword", corpid));
            batchBuilder.filter(termQuery(StringConstant.DEL, DelEnum.NORMAL.getDel()));
            batchBuilder.filter(termQuery(ProductStockSubFormEnum.getEsAttr(ProductStockSubFormEnum.WAREHOUSE_CHECKED), 1));
            batchBuilder.mustNot(termQuery(ProductStockSubFormEnum.getEsAttr(ProductStockSubFormEnum.BATCH), ""));
            List<PaasFormDataEntity> productStockEntityList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_PRODUCT_STOCK_INFO, batchBuilder, PaasFormDataEntity.class, Arrays.asList(ProductStockSubFormEnum.getAttrConnectData(ProductStockSubFormEnum.PRODUCT_ID), ProductStockSubFormEnum.getAttrConnectData(ProductStockSubFormEnum.WAREHOUSE_ID), ProductStockSubFormEnum.getAttrConnectData(ProductStockSubFormEnum.WAREHOUSE_NAME), ProductStockSubFormEnum.getAttrConnectData(ProductStockSubFormEnum.BATCH), ProductStockSubFormEnum.getAttrConnectData(ProductStockSubFormEnum.NUM), ProductStockSubFormEnum.getAttrConnectData(ProductStockSubFormEnum.PRODUCE_DATE), ProductStockSubFormEnum.getAttrConnectData(ProductStockSubFormEnum.GUARANTEE_PERIOD), ProductStockSubFormEnum.getAttrConnectData(ProductStockSubFormEnum.COST)));
            List<ProductStockEntity> productStockEntities = transferSubFormHelper.transferFormDataToProductStockList(productStockEntityList);
            for (ProductStockEntity entity : productStockEntities) {
                if (Objects.isNull(entity.getBatch())) {
                    continue;
                }
                String key = entity.getProductId() + "_" + entity.getWarehouseId();
                Double allBatchNum = productWarehouseBatchNumMap.getOrDefault(key, 0D);
                allBatchNum = Arith.add(allBatchNum, entity.getNum());
                productWarehouseBatchNumMap.put(key, allBatchNum);
                String batchKey = entity.getProductId() + "_" + entity.getWarehouseId() + "_" + entity.getBatch() + "_" + entity.getProduceDate() + "_" + entity.getGuaranteePeriod();
                Double num = batchNumMap.getOrDefault(batchKey, 0D);
                num = Arith.add(num, entity.getNum());
                batchNumMap.put(batchKey, num);
                batchCostMap.put(batchKey, Objects.isNull(entity.getCost()) ? 0D : entity.getCost());
                warehouseNameMap.put(entity.getWarehouseId(),entity.getWarehouseName());
            }
        }
        StringBuilder warehouseErrStringBuilder = new StringBuilder();
        StringBuilder warehouseEmptyErrStringBuilder = new StringBuilder();
        Map<String,Double> productNumMap = new HashMap<>();
        Map<String,Double> productBatchNumMap = new HashMap<>();
        for (int i = 0; i < productArray.size(); i++) {
            JSONObject productObj = productArray.getJSONObject(i);
            Long productId = productObj.getLong(SelectProductEnum.PRODUCT.getAttr());
            Double rate = productObj.getDouble(SelectProductEnum.RATE.getAttr());
            Long warehouseId;
            if (warehouseFromOut){
                warehouseId = pageWarehouseId;
            }else {
                SaveFormatHelp.formatLinkBusiness4Save(productObj,SelectProductEnum.WAREHOUSE.getAttr(),"warehouseName",FieldTypeEnum.LINK_BUSINESS_SINGLE.getType());
                warehouseId = productObj.getLong(SelectProductEnum.WAREHOUSE.getAttr());
            }
            String batch = productObj.getString(SelectProductEnum.BATCH.getAttr());
            Double productNum = productObj.getDouble(SelectProductEnum.NUM.getAttr());
            ProductEntityExt productEntityExt = productMap.get(productId);
            if (Objects.isNull(productEntityExt)) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, I18nMessageUtil.getMessage(I18nStringConstant.PRODUCT_NOT_EXIST));
            }
            JSONObject data = productEntityExt.getData();
            String productName = data.getString(ProductEnum.NAME.getAttr());
            Long parentId = data.getLong(ProductEnum.PARENT_ID.getAttr()) == null ? 0L : data.getLong(ProductEnum.PARENT_ID.getAttr());
            List<Long> parentIdList = Arrays.asList(parentId);
            Map<Long, List<SpecificationEntity>> specificationMap = specificationModel.getSpecificationMap(corpid, parentIdList);
            String specStr = formDataHelp.decodeSpec(data.getString(ProductEnum.SPECIFICATION.getAttr()), data.getString(ProductEnum.SPECIFICATION_LINK_TEXT.getAttr()), specificationMap.getOrDefault(parentId,new ArrayList<>()));
            String appendDecodeSpec = StringUtil.isEmpty(specStr) ? " " : " (" + specStr + ") ";
//            if (appendDecodeSpec.contains("%")) {
//                appendDecodeSpec = appendDecodeSpec.replace("%", "%%");
//            }
            if (Objects.isNull(batch) || batch.isEmpty()){
                String warehouseKey = productId + "_" + warehouseId;
                Double productWarehouseNum = productNumMap.getOrDefault(warehouseKey, 0D);
                productNum = Arith.add(productNum,productWarehouseNum);
                productNumMap.put(warehouseKey,productNum);
                if (warehouseNumMap.containsKey(warehouseKey)){
                    if (replaceCost) {
                        productObj.put(SelectProductEnum.COST.getAttr(), warehouseCostMap.getOrDefault(warehouseKey, 0D));
                    }
                    Double stock = warehouseNumMap.get(warehouseKey);
                    Double batchStock = productWarehouseBatchNumMap.getOrDefault(warehouseKey, 0D);
                    if (productWarehouseBatchNumMap.containsKey(warehouseKey)){
                        //批次表没有对应的空批次记录的话,就不校验了
                        if (Arith.sub(stock, batchStock) < productNum){
                            //warehouseErrStringBuilder.append("产品:").append(productName).append(decodeSpecification).append("大于").append(warehouseNameMap.getOrDefault(warehouseId,"")).append("仓库库存数量");
                            warehouseErrStringBuilder.append(I18nMessageUtil.getMessage(CommonConstant.PRODUCT) + ":").append(productName).append(appendDecodeSpec).append(I18nMessageUtil.getMessage(I18nStringConstant.INSUFFICIENT_STOCK));
                        }
                    }else {
                        if (stock < productNum){
                            warehouseErrStringBuilder.append(I18nMessageUtil.getMessage(CommonConstant.PRODUCT) + ":").append(productName).append(appendDecodeSpec).append(I18nMessageUtil.getMessage(CommonConstant.MORE_THAN)).append(warehouseNameMap.getOrDefault(warehouseId,"")).append(I18nMessageUtil.getMessage(I18nStringConstant.WAREHOUSE_STOCK_NUM));
                        }
                    }
                }else {
                    warehouseEmptyErrStringBuilder.append(I18nMessageUtil.getMessage(CommonConstant.PRODUCT) + ":").append(productName).append(appendDecodeSpec).append(I18nMessageUtil.getMessage(CommonConstant.NOT_IN)).append(warehouseNameMap.getOrDefault(warehouseId,"")).append(I18nMessageUtil.getMessage(I18nStringConstant.IN_WAREHOUSE));
                }
            }else {
                String batchKey = BatchKeyHelp.getBatchKey(productId, warehouseId, batch, productObj.getLong(SelectProductEnum.PRODUCE_DATE.getAttr()),
                        FastJsonHelper.getLongOrDefaultFromFormData(productObj, SelectProductEnum.GUARANTEE_PERIOD.getAttr(), 0L) * TimeConstant.SECONDS_PER_DAY);
                Double productWarehouseNum = productBatchNumMap.getOrDefault(batchKey, 0D);
                productNum = Arith.add(productNum,productWarehouseNum);
                productBatchNumMap.put(batchKey,productNum);
                if (batchNumMap.containsKey(batchKey)){
                    if (replaceCost) {
                        productObj.put(SelectProductEnum.COST.getAttr(), batchCostMap.getOrDefault(batchKey, 0D));
                    }
                    Double stock = batchNumMap.get(batchKey);
                    if (stock < productNum){
                        warehouseErrStringBuilder.append(I18nMessageUtil.getMessage(CommonConstant.PRODUCT) + ":").append(productName).append(appendDecodeSpec).append("," + I18nMessageUtil.getMessage(I18nStringConstant.BATCH)+ ":").append(batch).append(I18nMessageUtil.getMessage(CommonConstant.MORE_THAN)).append(warehouseNameMap.getOrDefault(warehouseId,"")).append(I18nMessageUtil.getMessage(I18nStringConstant.WAREHOUSE_STOCK_NUM));
                    }
                }else {
                    warehouseEmptyErrStringBuilder.append(I18nMessageUtil.getMessage(CommonConstant.PRODUCT) + ":").append(productName).append(appendDecodeSpec).append("," + I18nMessageUtil.getMessage(I18nStringConstant.BATCH)+ ":").append(batch).append(I18nMessageUtil.getMessage(CommonConstant.NOT_IN)).append(warehouseNameMap.getOrDefault(warehouseId,"")).append(I18nMessageUtil.getMessage(I18nStringConstant.IN_WAREHOUSE));
                }
            }
            productObj.put(SelectProductEnum.TOTAL_COST.getAttr(), Arith.mul(productObj.getDoubleValue(SelectProductEnum.COST.getAttr()), productObj.getDoubleValue(SelectProductEnum.NUM.getAttr())));
            if (Objects.nonNull(rate)) {
                productObj.put(SelectProductEnum.BUSINESS_COST.getAttr(), Arith.mul(productObj.getDoubleValue(SelectProductEnum.COST.getAttr()), rate));
            }
        }
        warehouseErrStringBuilder.append(warehouseEmptyErrStringBuilder);
        if (!warehouseErrStringBuilder.toString().isEmpty()){
//            String errorMsg = warehouseErrStringBuilder.toString();
//            if (errorMsg.contains("%")) {
//                errorMsg = errorMsg.replace("%", "%%");
//            }
//            throw new XbbException(OutstockErrorCodeEnum.API_ERROR_212013, errorMsg);
            throw new XbbException(OutstockErrorCodeEnum.API_ERROR_212013, warehouseErrStringBuilder.toString());
        }
    }

    /**
     * 校验产品存在合法
     * @param corpid 公司id
     * @param productIdLit 产品idList
     * @throws XbbException
     */
    public Map<Long, String> productExit(String corpid, List<Long> productIdLit) throws XbbException {
        if (productIdLit.isEmpty()){
            return new HashMap<>();
        }
        //TODO 可以抽公共方法
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword",corpid));
        boolQueryBuilder.filter(termsQuery(FieldTypeEnum.DATAID.getAlias(),productIdLit));
//        boolQueryBuilder.filter(termQuery(StringConstant.DEL,DelEnum.NORMAL.getDel()));
        List<PaasFormDataEntityExt> childList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_PRODUCT,boolQueryBuilder,PaasFormDataEntityExt.class,Arrays.asList(FieldTypeEnum.DATAID.getAlias(),ProductEnum.getAttrConnectData(ProductEnum.NAME),ProductEnum.getAttrConnectData(ProductEnum.SPECIFICATION),ProductEnum.getAttrConnectData(ProductEnum.SPECIFICATION_LINK_TEXT),StringConstant.DEL));
        Map<Long, PaasFormDataEntityExt> productMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<Long,String> pageProductMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (PaasFormDataEntityExt entityExt : childList) {
            productMap.put(entityExt.getDataId(),entityExt);
            JSONObject data = entityExt.getData();
            String name = data.getString(ProductEnum.NAME.getAttr());
            Long parentId = data.getLong(ProductEnum.PARENT_ID.getAttr()) == null ? 0L : entityExt.getData().getLong(ProductEnum.PARENT_ID.getAttr());
            List<Long> parentIdList = Arrays.asList(parentId);
            Map<Long, List<SpecificationEntity>> specificationMap = specificationModel.getSpecificationMap(corpid, parentIdList);
            String specStr = formDataHelp.decodeSpec(data.getString(ProductEnum.SPECIFICATION.getAttr()), data.getString(ProductEnum.SPECIFICATION_LINK_TEXT.getAttr()), specificationMap.getOrDefault(parentId,new ArrayList<>()));
            if (StringUtil.isEmpty(specStr)) {
                pageProductMap.put(entityExt.getDataId(), name);
            } else {
                pageProductMap.put(entityExt.getDataId(), name + "（" + specStr + "）");
            }
        }
        StringBuilder productIsDel = new StringBuilder();
        StringBuilder productIsExit = new StringBuilder();

        for (Long productId : productIdLit){
            if (productMap.containsKey(productId)){
                PaasFormDataEntityExt product = productMap.get(productId);
                if (Objects.equals(product.getDel(),DelEnum.DELETE.getDel())){
                    String name = FastJsonHelper.getStringOrDefaultFromFormData(product.getData(), ProductEnum.NAME.getAttr(), "");
                    productIsDel.append(name).append(",");
                }
            }else {
                productIsExit.append(pageProductMap.get(productId)).append(",");
            }
        }

        String stringBuilder = "";
        if (productIsDel.length() > 0){
            stringBuilder = I18nMessageUtil.getMessage(CommonConstant.PRODUCT) + productIsDel + I18nMessageUtil.getMessage(I18nStringConstant.PRODUCT_DELETE);
        }
        if (productIsExit.length() > 0){
            stringBuilder = I18nMessageUtil.getMessage(CommonConstant.PRODUCT) + productIsExit + I18nMessageUtil.getMessage(I18nStringConstant.PRODUCT_NOT_EXIST);
        }
        if (!stringBuilder.isEmpty()){
            throw new XbbException(ProductErrorCodeEnum.API_ERROR_205018.getCode(), stringBuilder);
        }
        return pageProductMap;
    }

    /**
     * 校验产品存在合法
     * @param corpid 公司id
     * @param productIdLit 产品idList
     * @throws XbbException
     */
    public Map<Long, String> productExitContract(JSONArray productList, String corpid, List<Long> productIdLit) throws XbbException {
        if (productIdLit.isEmpty()){
            return new HashMap<>();
        }
        //TODO 可以抽公共方法
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword",corpid));
        boolQueryBuilder.filter(termsQuery(FieldTypeEnum.DATAID.getAlias(),productIdLit));
//        boolQueryBuilder.filter(termQuery(StringConstant.DEL,DelEnum.NORMAL.getDel()));
        List<PaasFormDataEntityExt> childList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_PRODUCT,boolQueryBuilder,PaasFormDataEntityExt.class,Arrays.asList(FieldTypeEnum.DATAID.getAlias(),ProductEnum.getAttrConnectData(ProductEnum.NAME),ProductEnum.getAttrConnectData(ProductEnum.SPECIFICATION),ProductEnum.getAttrConnectData(ProductEnum.SPECIFICATION_LINK_TEXT),StringConstant.DEL,ProductEnum.getAttrConnectData(ProductEnum.COST)));
        Map<Long, PaasFormDataEntityExt> productMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<Long,String> pageProductMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (PaasFormDataEntityExt entityExt : childList) {
            productMap.put(entityExt.getDataId(),entityExt);
            JSONObject data = entityExt.getData();
            String name = data.getString(ProductEnum.NAME.getAttr());
            Long parentId = data.getLong(ProductEnum.PARENT_ID.getAttr()) == null ? 0L : entityExt.getData().getLong(ProductEnum.PARENT_ID.getAttr());
            List<Long> parentIdList = Arrays.asList(parentId);
            Map<Long, List<SpecificationEntity>> specificationMap = specificationModel.getSpecificationMap(corpid, parentIdList);
            String specStr = formDataHelp.decodeSpec(data.getString(ProductEnum.SPECIFICATION.getAttr()), data.getString(ProductEnum.SPECIFICATION_LINK_TEXT.getAttr()), specificationMap.getOrDefault(parentId,new ArrayList<>()));
            if (StringUtil.isEmpty(specStr)) {
                pageProductMap.put(entityExt.getDataId(), name);
            } else {
                pageProductMap.put(entityExt.getDataId(), name + "（" + specStr + "）");
            }
        }
        StringBuilder productIsDel = new StringBuilder();
        StringBuilder productIsExit = new StringBuilder();

        for (int i=0; i<productList.size(); i++){
            JSONObject json = productList.getJSONObject(i);
            Long productId = json.getLong(SelectProductEnum.PRODUCT.getAttr());
            if (productMap.containsKey(productId)){
                PaasFormDataEntityExt product = productMap.get(productId);
                if (Objects.equals(product.getDel(),DelEnum.DELETE.getDel())){
                    String name = FastJsonHelper.getStringOrDefaultFromFormData(product.getData(), ProductEnum.NAME.getAttr(), "");
                    productIsDel.append(name).append(",");
                }else {
                    //产品成本赋值
                    json.put(ContractEnum.ESTIMATE_COST.getSaasAttr(), product.getData().getDouble(ProductEnum.COST.getAttr()));
                }
            }else {
                productIsExit.append(pageProductMap.get(productId)).append(",");
            }
        }

        String stringBuilder = "";
        if (productIsDel.length() > 0){
            stringBuilder = I18nMessageUtil.getMessage(CommonConstant.PRODUCT) + productIsDel + I18nMessageUtil.getMessage(I18nStringConstant.PRODUCT_DELETE);
        }
        if (productIsExit.length() > 0){
            stringBuilder = I18nMessageUtil.getMessage(CommonConstant.PRODUCT) + productIsExit + I18nMessageUtil.getMessage(I18nStringConstant.PRODUCT_NOT_EXIST);
        }
        if (!stringBuilder.isEmpty()){
            throw new XbbException(ProductErrorCodeEnum.API_ERROR_205018.getCode(), stringBuilder);
        }
        return pageProductMap;
    }


    public static Double calculateCost(Double originalStock, Double originalCost, Double addStock, Double addCost, Integer type) {
        boolean increase = Objects.equals(type, 1);
        if (Objects.isNull(originalStock) || Objects.isNull(originalCost)) {
            return null;
        }
        double totalStock;
        if (increase) {
            totalStock = Arith.add(originalStock, addStock);
        } else {
            totalStock = Arith.sub(originalStock, addStock);
        }
        if (Double.compare(totalStock, 0) <= 0) {
            return originalCost;
        }

        //原来的总成本
        double originalTotalCost = Arith.mul(originalStock, originalCost);
        //新入库的总成本
        double addTotalCost = Arith.mul(addStock, addCost);
        //总成本
        double totalCost;
        if (increase) {
            totalCost = Arith.add(originalTotalCost, addTotalCost);
        } else {
            totalCost = Arith.sub(originalTotalCost, addTotalCost);
        }

        return Arith.div(totalCost, totalStock);
    }

    public boolean judgeAllOutBound(Map<Long, Double> productNumMap, Long refId, int type, String corpid, List<ProductSavePojo> productSavePojoList, Long outstockId) throws XbbException {
//        BoolQueryBuilder boolQueryBuilder = boolQuery();
//        boolQueryBuilder.filter(termQuery("corpid.keyword",corpid));
//        boolQueryBuilder.filter(termQuery(StringConstant.DEL,DelEnum.NORMAL.getDel()));
//        boolQueryBuilder.filter(termQuery(OutstockEnum.getEsAttr4Keyword(OutstockEnum.TYPE),type));
//        boolQueryBuilder.filter(termQuery(OutstockEnum.getEsAttr4Keyword(OutstockEnum.REF_ID),refId));
//        boolQueryBuilder.mustNot(termQuery(FieldTypeEnum.DATAID.getAlias(),outstockId));
//        List<PaasFormDataEntityExt> outstockList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_OUTSTOCK,boolQueryBuilder,PaasFormDataEntityExt.class,Collections.singletonList(FieldTypeEnum.DATAID.getAlias()));
//        List<Long> outstockIdList = new ArrayList<>();
//        for (PaasFormDataEntityExt entityExt : outstockList){
//            outstockIdList.add(entityExt.getDataId());
//        }
//        if (!outstockIdList.isEmpty()) {
            Map<String,Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("corpid", corpid);
            param.put("del", 0);
            param.put("type",type);
            param.put("refId",refId);
            param.put("negOutWarehouseId",outstockId);
            List<OutstockProductEntity> outstockProductEntityList = outstockProductModel.findEntitys(param);
//        }
        for (OutstockProductEntity entity : outstockProductEntityList){
            Long productId = entity.getProductId();
            if (!productNumMap.containsKey(productId)){
                continue;
            }
            Double outstockNum = entity.getProductNum();
            Double productNum = productNumMap.get(productId);
            double num = Arith.sub(productNum, outstockNum);
            if (num < 0.00000001){
                productNumMap.remove(productId);
            }else {
                productNumMap.put(productId,num);
            }
        }
        for (ProductSavePojo productSavePojo : productSavePojoList){
            Long productId = productSavePojo.getId();
            if (!productNumMap.containsKey(productId)){
                continue;
            }
            Double outstockNum = productSavePojo.getNum();
            Double productNum = productNumMap.get(productId);
            double num = Arith.sub(productNum, outstockNum);
            if (num < 0.00000001){
                productNumMap.remove(productId);
            }else {
                productNumMap.put(productId,num);
            }
        }
        return productNumMap.isEmpty();
    }


    public boolean judgeAllInBound(Map<Long,Double> productNumMap, Long refId, int type, String corpid, List<ProductSavePojo> productSavePojoList, Long instockId) throws XbbException {
//        BoolQueryBuilder boolQueryBuilder = boolQuery();
//        boolQueryBuilder.filter(termQuery("corpid.keyword",corpid));
//        boolQueryBuilder.filter(termQuery(StringConstant.DEL,DelEnum.NORMAL.getDel()));
//        boolQueryBuilder.filter(termQuery(InstockEnum.getEsAttr4Keyword(InstockEnum.TYPE),type));
//        boolQueryBuilder.filter(termQuery(InstockEnum.getEsAttr4Keyword(InstockEnum.REF_ID),refId));
//        boolQueryBuilder.mustNot(termQuery(FieldTypeEnum.DATAID.getAlias(),instockId));
//        List<PaasFormDataEntityExt> instockList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_INSTOCK,boolQueryBuilder,PaasFormDataEntityExt.class,Collections.singletonList(FieldTypeEnum.DATAID.getAlias()));
//        List<Long> instockIdList = new ArrayList<>();
//        for (PaasFormDataEntityExt entityExt : instockList){
//            instockIdList.add(entityExt.getDataId());
//        }
//        List<InstockProductEntity> instockProductEntityList = new ArrayList<>();
//        if (!instockIdList.isEmpty()) {
        Map<String,Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid", corpid);
        param.put("del", 0);
        param.put("type",type);
        param.put("refId",refId);
        param.put("negIntoWarehouseId",instockId);
//        param.put("instockIdIn", instockIdList);
        List<InstockProductEntity> instockProductEntityList = instockProductModel.findEntitys(param);
//        }
        for (InstockProductEntity entity : instockProductEntityList){
            Long productId = entity.getProductId();
            if (!productNumMap.containsKey(productId)){
                continue;
            }
            Double instockNum = entity.getProductNum();
            Double productNum = productNumMap.get(productId);
            double num = Arith.sub(productNum, instockNum);
            if (num < 0.00000001){
                productNumMap.remove(productId);
            }else {
                productNumMap.put(productId,num);
            }
        }
        for (ProductSavePojo productSavePojo : productSavePojoList){
            Long productId = productSavePojo.getId();
            if (!productNumMap.containsKey(productId)){
                continue;
            }
            Double instockNum = productSavePojo.getNum();
            Double productNum = productNumMap.get(productId);
            double num = Arith.sub(productNum, instockNum);
            if (num < 0.00000001){
                productNumMap.remove(productId);
            }else {
                productNumMap.put(productId,num);
            }
        }
        return productNumMap.isEmpty();
    }

    public ContractProductUpdateOutstockNumVO recalculateOutstockNum(List<ContractEntityExt> contractList, ContractOutstockDeleteVO contractOutstockDeleteVO) {
        ContractProductUpdateOutstockNumVO contractProductUpdateOutstockNumVO = new ContractProductUpdateOutstockNumVO();
        List<ContractProductEntity> contractProductUpdateOutstockNumList = new ArrayList<>();
        List<Long> needUpdateOutboundStatusContractList = new ArrayList<>();

        for (ContractEntityExt contractEntityExt : contractList) {
            Long contractId = contractEntityExt.getId();
            List<OutstockProductEntity> outstockProductEntityList = contractOutstockDeleteVO.getContractOutstockProductMap().get(contractId);
            List<ContractProductEntity> contractProductEntityList = contractOutstockDeleteVO.getContractProductMap().get(contractId);

            // 合并相同产品id的产品出库数量
            Map<Long, Double> contractProductOutstockNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            for (OutstockProductEntity outstockProductEntity : outstockProductEntityList) {
                Double outstockNum = contractProductOutstockNumMap.getOrDefault(outstockProductEntity.getProductId(), 0D);
                outstockNum = Arith.add(outstockNum, outstockProductEntity.getProductNum());
                contractProductOutstockNumMap.put(outstockProductEntity.getProductId(), outstockNum);
            }
            // 合并相同产品id的产品数量
            Map<Long, Double> contractProductNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            for (ContractProductEntity contractProductEntity : contractProductEntityList) {
                Double productNum = contractProductNumMap.getOrDefault(contractProductEntity.getProductId(), 0D);
                productNum = Arith.add(productNum, contractProductEntity.getProductNum());
                contractProductNumMap.put(contractProductEntity.getProductId(), productNum);
            }
            for (ContractProductEntity contractProductEntity : contractProductEntityList) {
                Long productId = contractProductEntity.getProductId();
                if (!contractProductOutstockNumMap.containsKey(productId)) {
                    // 产品还没有出库
                    needUpdateOutboundStatusContractList.add(contractProductEntity.getContractId());
                    break;
                } else {
                    if (contractProductOutstockNumMap.get(productId) < contractProductNumMap.get(productId)) {
                        needUpdateOutboundStatusContractList.add(contractProductEntity.getContractId());
                        break;
                    }
                }
            }

            // 给合同产品分配出库数量，如果存在相同的产品，则先将一个产品分配满(出库完)在分配给另一个产品
            for (ContractProductEntity contractProductEntity : contractProductEntityList) {
                if (contractProductOutstockNumMap.containsKey(contractProductEntity.getProductId())) {
                    Double productNum = contractProductEntity.getProductNum();
                    Double amendNum = contractProductOutstockNumMap.get(contractProductEntity.getProductId());
                    Double outNum = Arith.sub(amendNum, productNum);
                    if (outNum > 0) {
                        outNum = productNum;
                        contractProductOutstockNumMap.put(contractProductEntity.getProductId(), outNum);
                    }else {
                        outNum = amendNum;
                        contractProductOutstockNumMap.remove(contractProductEntity.getProductId());
                    }
                    contractProductEntity.setOutstockNum(outNum);
                    contractProductEntity.setContractOutstockNum(outNum);
                    contractProductEntity.setUpdateTime(DateTimeUtil.getInt());
                    contractProductUpdateOutstockNumList.add(contractProductEntity);
                }else {
                    contractProductEntity.setOutstockNum(0D);
                    contractProductEntity.setContractOutstockNum(0D);
                    contractProductEntity.setUpdateTime(DateTimeUtil.getInt());
                    contractProductUpdateOutstockNumList.add(contractProductEntity);
                }
            }
        }

        contractProductUpdateOutstockNumVO.setContractProductUpdateOutstockNumList(contractProductUpdateOutstockNumList);
        contractProductUpdateOutstockNumVO.setNeedUpdateOutboundStatusContractList(needUpdateOutboundStatusContractList);
        return contractProductUpdateOutstockNumVO;
    }

    public ContractProductUpdateOutstockNumVO recalculateNewOutstockNum(List<ContractEntityExt> contractList, ContractOutstockDeleteVO contractOutstockDeleteVO) {
        ContractProductUpdateOutstockNumVO contractProductUpdateOutstockNumVO = new ContractProductUpdateOutstockNumVO();
        List<ContractProductEntity> contractProductUpdateOutstockNumList = new ArrayList<>();
        List<Long> needUpdateOutboundStatusContractList = new ArrayList<>();

        for (ContractEntityExt contractEntityExt : contractList) {
            Long contractId = contractEntityExt.getId();
            List<OutstockProductEntity> outstockProductEntityList = contractOutstockDeleteVO.getContractOutstockProductMap().get(contractId);
            List<ContractProductEntity> contractProductEntityList = contractOutstockDeleteVO.getContractProductMap().get(contractId);

            // 合同产品和该产品的出库数量的对应关系
            Map<Long, Double> contractProductOutstockNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            for (OutstockProductEntity outstockProductEntity : outstockProductEntityList) {
                Long refProductId = outstockProductEntity.getRefProductId();
                Double outstockNum = contractProductOutstockNumMap.getOrDefault(refProductId, 0D);
                outstockNum = Arith.add(outstockNum, outstockProductEntity.getProductNum());
                contractProductOutstockNumMap.put(refProductId, outstockNum);
            }
            for (ContractProductEntity contractProductEntity : contractProductEntityList) {
                Long refProductId = contractProductEntity.getId();
                if (!contractProductOutstockNumMap.containsKey(refProductId)) {
                    // 产品还没有出库
                    needUpdateOutboundStatusContractList.add(contractProductEntity.getContractId());
                    break;
                } else {
                    if (contractProductOutstockNumMap.get(refProductId) < contractProductEntity.getProductNum()) {
                        needUpdateOutboundStatusContractList.add(contractProductEntity.getContractId());
                        break;
                    }
                }
            }

            for (ContractProductEntity contractProductEntity : contractProductEntityList) {
                if (contractProductOutstockNumMap.containsKey(contractProductEntity.getId())) {
                    contractProductEntity.setOutstockNum(contractProductOutstockNumMap.get(contractProductEntity.getId()));
                    contractProductEntity.setContractOutstockNum(contractProductOutstockNumMap.get(contractProductEntity.getId()));
                } else {
                    contractProductEntity.setOutstockNum(0D);
                    contractProductEntity.setContractOutstockNum(0D);
                }
                contractProductEntity.setUpdateTime(DateTimeUtil.getInt());
                contractProductUpdateOutstockNumList.add(contractProductEntity);
            }
        }

        contractProductUpdateOutstockNumVO.setContractProductUpdateOutstockNumList(contractProductUpdateOutstockNumList);
        contractProductUpdateOutstockNumVO.setNeedUpdateOutboundStatusContractList(needUpdateOutboundStatusContractList);
        return contractProductUpdateOutstockNumVO;
    }

    /**
     * 产品是否在库存中
     * 仅用于不可重复产品的
     * @param corpid
     * @param jsonArray
     * @throws XbbException
     */
    public void productWarehouseExit(String corpid, JSONArray jsonArray) throws XbbException {
        List<Long> productIdLit = new ArrayList<>();
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword",corpid));
        boolQueryBuilder.filter(termQuery(StringConstant.DEL,DelEnum.NORMAL.getDel()));
        boolQueryBuilder.filter(termQuery(ProductWarehouseSubFormEnum.getEsAttr(ProductWarehouseSubFormEnum.WAREHOUSE_CHECKED), 1));
        for (int i = 0;i < jsonArray.size();i++) {
            JSONObject jsonObject = jsonArray.getJSONObject(i);
            Long productId = jsonObject.getLong(SelectProductEnum.PRODUCT.getAttr());
            Long warehouseId = jsonObject.getLong(SelectProductEnum.WAREHOUSE.getAttr());
            BoolQueryBuilder innerBoolQueryBuilder = boolQuery();
            innerBoolQueryBuilder.filter(termQuery(ProductWarehouseSubFormEnum.getEsAttr(ProductWarehouseSubFormEnum.PRODUCT_ID), productId));
            innerBoolQueryBuilder.filter(termQuery(ProductWarehouseSubFormEnum.getEsAttr(ProductWarehouseSubFormEnum.WAREHOUSE_ID), warehouseId));
            boolQueryBuilder.should(innerBoolQueryBuilder);
            productIdLit.add(productId);
        }
        boolQueryBuilder.minimumShouldMatch(MINI_MUM_SHOULD_MATCH);

        BoolQueryBuilder boolQueryBuilder2 = boolQuery();
        boolQueryBuilder2.filter(termQuery("corpid.keyword",corpid));
        boolQueryBuilder2.filter(termsQuery(FieldTypeEnum.DATAID.getAlias(),productIdLit));
        List<PaasFormDataEntityExt> childList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_PRODUCT,boolQueryBuilder2,PaasFormDataEntityExt.class,Arrays.asList(FieldTypeEnum.DATAID.getAlias(),ProductEnum.getAttrConnectData(ProductEnum.NAME),ProductEnum.getAttrConnectData(ProductEnum.SPECIFICATION),ProductEnum.getAttrConnectData(ProductEnum.SPECIFICATION_LINK_TEXT),StringConstant.DEL));
        Map<Long, PaasFormDataEntityExt> productMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<Long,String> pageProductMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (PaasFormDataEntityExt entityExt : childList) {
            productMap.put(entityExt.getDataId(),entityExt);
            JSONObject data = entityExt.getData();
            String name = data.getString(ProductEnum.NAME.getAttr());
            Long parentId = data.getLong(ProductEnum.PARENT_ID.getAttr()) == null ? 0L : entityExt.getData().getLong(ProductEnum.PARENT_ID.getAttr());
            List<Long> parentIdList = Arrays.asList(parentId);
            Map<Long, List<SpecificationEntity>> specificationMap = specificationModel.getSpecificationMap(corpid, parentIdList);
            String specStr = formDataHelp.decodeSpec(data.getString(ProductEnum.SPECIFICATION.getAttr()), data.getString(ProductEnum.SPECIFICATION_LINK_TEXT.getAttr()), specificationMap.getOrDefault(parentId,new ArrayList<>()));
            if (StringUtil.isEmpty(specStr)) {
                pageProductMap.put(entityExt.getDataId(), name);
            } else {
                pageProductMap.put(entityExt.getDataId(), name + "（" + specStr + "）");
            }
        }
        List<PaasFormDataEntity> productWarehouseEntityList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_PRODUCT_WAREHOUSE, boolQueryBuilder, PaasFormDataEntity.class, Collections.singletonList(ProductWarehouseSubFormEnum.getAttrConnectData(ProductWarehouseSubFormEnum.PRODUCT_ID)));
        List<ProductWarehouseEntity> productWarehouseEntities = transferSubFormHelper.transferFormDataToProductWarehouseList(productWarehouseEntityList);
        StringBuilder productIsNotExit = new StringBuilder();
        for (PaasFormDataEntityExt entityExt : childList) {
            Long productId = entityExt.getDataId();
            boolean has = false;
            for (ProductWarehouseEntity productWarehouseEntity : productWarehouseEntities){
                if (Objects.equals(productWarehouseEntity.getProductId(),productId)){
                    has = true;
                }
            }
            if (!has){
                productIsNotExit.append(pageProductMap.get(productId)).append(" ");
            }
        }
        if (productIsNotExit.length() > 0){
            throw new XbbException(OutstockErrorCodeEnum.API_ERROR_212013, OutstockErrorCodeEnum.API_ERROR_212013.getMsg(), productIsNotExit);
        }
//        if (productWarehouseEntityList.size() < jsonArray.size()){
//            throw new XbbException(OutstockErrorCodeEnum.API_ERROR_212013);
//        }
    }

    /**
     * 获取入库数量（包含审批中数量）
     * @param corpid 公司id
     * @param refId 关联业务id
     * @param type 关联业务类型
     * @throws XbbException
     * @return 已出库数量
     */
    public Map<String,Double> getInstockProduct(String corpid, Long refId,Integer type, Map<Long,String> pageProductMap) throws XbbException {
        Map<String,Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid", corpid);
        param.put("del", 0);
        param.put("refId", refId);
        param.put("type", type);
        // 已入库产品
        List<BusinessProductEntity> instockProductEntityList = instockProductModel.findProductNum(param);
        Map<String,Double> businessProductMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        boolean needMerge = false;
        for (BusinessProductEntity productEntity : instockProductEntityList) {
            if (Objects.isNull(productEntity.getRefProductId())) {
                needMerge = true;
            }
            if (!pageProductMap.containsKey(productEntity.getProductId())) {
                pageProductMap.put(productEntity.getProductId(), productEntity.getProductName());
            }
        }
        InstockTypeEnum instockTypeEnum = InstockTypeEnum.getByCode(type);
        if (!commonHelp.isOpenWorkFlow(corpid) && Objects.equals(instockTypeEnum, InstockTypeEnum.PURCHASE_INSTOCK)) {
            List<PaasProcessRelatedDataEntity> processDataList = paasProcessRelatedDataModel.getByRefIdAndBusinessType(refId, instockTypeEnum.getStockBusinessType(), corpid);
            Map<String, Double> processDataMap = new HashMap<>(processDataList.size());
            processDataList.forEach(item -> {
                String key = item.getFourthRefId() + "_" + item.getSecondRefId();
                Double num = processDataMap.getOrDefault(key, 0D);
                num = Arith.add(num, item.getValue());
                processDataMap.put(key, num);
            });
            instockProductEntityList.forEach(item -> {
                String key = item.getProductId() + "_" + item.getRefProductId();
                if (processDataMap.containsKey(key)) {
                    item.setProductNum(Arith.add(processDataMap.get(key), item.getProductNum()));
                    processDataMap.remove(key);
                }
            });
            // 只有审批中有的时候需要加到入库产品中
            processDataMap.forEach((key, value) -> {
                BusinessProductEntity businessProductEntity = new BusinessProductEntity();
                String[] args = key.split("_");
                businessProductEntity.setProductId(Long.valueOf(args[0]));
                businessProductEntity.setRefProductId(Long.valueOf(args[1]));
                businessProductEntity.setProductNum(value);
                instockProductEntityList.add(businessProductEntity);
            });
        }
        for (BusinessProductEntity entity : instockProductEntityList){
            String key;
            if (needMerge) {
                key = entity.getProductId().toString();
            } else {
                key = entity.getProductId() + "_" + entity.getRefProductId();
            }
            Double num  = businessProductMap.getOrDefault(key,0D);
            num = Arith.add(num , entity.getProductNum());
            businessProductMap.put(key,num);
        }
        return businessProductMap;
    }


    /**
     * 获取采购入库数量
     * @param corpid 公司id
     * @param refId 关联业务id
     * @param type 关联业务类型
     * @throws XbbException
     * @return 已出库数量
     */
    public Map<String,Double> getPurchaseInstockProduct(String corpid, Long refId,Integer type, boolean needMerge, Long dataId) throws XbbException {
        Map<String,Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid", corpid);
        param.put("del", 0);
        param.put("type",type);
        param.put("refId",refId);
        // 已入库产品
        // TODO 这里要获取实际已入库数量，不包括审批中数量
        List<BusinessProductEntity> instockProductEntityList = instockProductModel.findProductNum(param);
        Map<String,Double> businessProductMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (BusinessProductEntity entity : instockProductEntityList){
            String key;
            if (needMerge) {
                key = entity.getProductId().toString();
            } else {
                key = entity.getProductId() + "_" + entity.getId();
            }
            Double num  = businessProductMap.getOrDefault(key,0D);
            num = Arith.add(num , entity.getProductNum());
            businessProductMap.put(key,num);
        }

        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword",corpid));
        boolQueryBuilder.filter(termQuery(ReturnedPurchaseEnum.getEsAttr4Keyword(ReturnedPurchaseEnum.PURCHASE_SHEET_ID),refId));
        boolQueryBuilder.filter(termQuery(StringConstant.DEL,DelEnum.NORMAL.getDel()));
        if (Objects.nonNull(dataId) && !Objects.equals(dataId, 0L)) {
            boolQueryBuilder.mustNot(termQuery(FieldTypeEnum.DATAID.getAlias(), dataId));
        }
        List<PaasFormDataEntityExt> returnList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_RETURNED_PURCHASE,boolQueryBuilder,PaasFormDataEntityExt.class,Collections.singletonList(FieldTypeEnum.DATAID.getAlias()));
        Map<String,Double> returnMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        if (!returnList.isEmpty()) {
            List<Long> returnIdList = new ArrayList<>();
            returnList.forEach(item -> returnIdList.add(item.getDataId()));
            param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("corpid", corpid);
            param.put("del", 0);
            param.put("returnPurchaseIdIn", returnIdList);
            List<BusinessProductEntity> refundProductList = returnedPurchaseProductModel.findProductNum(param);
            for (BusinessProductEntity entity : refundProductList){
                String key;
                if (needMerge) {
                    key = entity.getProductId().toString();
                } else {
                    key = entity.getProductId() + "_" + entity.getRefProductId();
                }
                Double num = returnMap.getOrDefault(key,0D);
                num = Arith.add(num , entity.getProductNum());
                returnMap.put(key,num);
            }
        }
        for (Map.Entry<String, Double> entry : businessProductMap.entrySet()){
            String key = entry.getKey();
            Double value = entry.getValue();
            if (returnMap.containsKey(key)){
                Double num = returnMap.get(key);
                businessProductMap.put(key,Arith.sub(value,num));
            }
        }
        return businessProductMap;
    }

    /**
     * 退货退款校验未退产品
     * @param corpid
     * @param contractId
     * @param refundId
     * @return
     * @throws XbbException
     */
    public Map<String,Double> getContractUnRefund(String corpid, Long contractId, Long refundId, boolean needMerge) throws XbbException {
        //退货退款未退数量
        PaasAppEntity paasAppEntity = paasAppModel.getByAlias(XbbRefTypeEnum.JXC.getAlias(), corpid);
        boolean isOpenJxc = false;
        if (Objects.nonNull(paasAppEntity)) {
            isOpenJxc = paasAppEntity.getEnable() == 1;
        }
        Map<String,Double> unRefundMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        if (isOpenJxc){
            List<OutstockProductEntity> outstockProductEntityList = searchContractUnRefund(corpid,contractId,true,refundId);
            for (OutstockProductEntity entity : outstockProductEntityList){
                String key;
                if (needMerge) {
                    key = entity.getProductId().toString();
                } else {
                    key = entity.getProductId() + "_" + entity.getId();
                }
                Double num  = unRefundMap.getOrDefault(key,0D);
                num += entity.getProductNum();
                unRefundMap.put(key,num);
            }
        }else {
            List<ContractProductEntity> contractProductEntityList = searchContractUnRefund(corpid,contractId,false,refundId);
            for (ContractProductEntity entity : contractProductEntityList){
                String key;
                if (needMerge) {
                    key = entity.getProductId().toString();
                } else {
                    key = entity.getProductId() + "_" + entity.getId();
                }
                Double num  = unRefundMap.getOrDefault(key,0D);
                num += entity.getProductNum();
                unRefundMap.put(key,num);
            }
        }
        return unRefundMap;
    }
    /**
     * 退货退款关联单据产品检索
     * @param corpid 公司id
     * @param contractId 关联合同id
     * @param isOpenJxc 是否开启jxc
     * @param refundId 退货退款id，编辑有，新建null，校验时排除自身
     * @return
     * @throws XbbException
     */
    public List searchContractUnRefund(String corpid, Long contractId, boolean isOpenJxc, Long refundId) throws XbbException {
        //该合同下已存在的退货退款
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword",corpid));
        boolQueryBuilder.filter(termQuery(RefundEnum.getEsAttr4Keyword(RefundEnum.CONTRACT_ID),contractId));
        if (Objects.nonNull(refundId)) {
            boolQueryBuilder.mustNot(termQuery(FieldTypeEnum.DATAID.getAlias(),refundId));
        }
        boolQueryBuilder.filter(termQuery(StringConstant.DEL,DelEnum.NORMAL.getDel()));
        List<PaasFormDataEntityExt> refundList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_REFUND,boolQueryBuilder,PaasFormDataEntityExt.class,Collections.singletonList(RefundEnum.getAttrConnectData(RefundEnum.RED_CONTRACT_ID)));
        Map<String,Double> refundMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        // 退货退款需要合同的单价，还需要加权平均
        Map<String,Double> priceMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        boolean needMerge = false;
        if (!refundList.isEmpty()) {
            List<Long> refundIdList = new ArrayList<>();
            refundList.stream().filter(v -> !Objects.isNull(v.getData())).forEach(item -> refundIdList.add(item.getData().getLong(RefundEnum.RED_CONTRACT_ID.getAttr())));
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("corpid", corpid);
            param.put("del", 0);
            param.put("contractIdIn", refundIdList);
            param.put("type", ContractProductTypeEnum.REFUND.getCode());
            List<BusinessProductEntity> refundProductList = contractProductModel.findProductNum(param);
            for (BusinessProductEntity entity : refundProductList) {
                if (Objects.isNull(entity.getRefProductId())) {
                    needMerge = true;
                    break;
                }
            }
            for (BusinessProductEntity entity : refundProductList){
                String key;
                if (needMerge) {
                    key = entity.getProductId().toString();
                } else {
                    key = entity.getProductId() + "_" + entity.getRefProductId();
                }
                Double num  = refundMap.getOrDefault(key,0D);
                Double originNum = num;
                num = Arith.add(num, Math.abs(entity.getProductNum()));
                if (priceMap.containsKey(key)) {
                    Double originPrice = priceMap.get(key);
                    Double totalPrice = Arith.mul(originNum, originPrice);
                    Double price = entity.getPrice() == null ? 0D : entity.getPrice();
                    Double averagePrice =  Arith.div(Arith.add(totalPrice, Arith.mul(entity.getProductNum(), price)), num);
                    priceMap.put(key,averagePrice);
                } else {
                    Double price = entity.getPrice() == null ? 0D : entity.getPrice();
                    priceMap.put(key,price);
                }
                refundMap.put(key,num);
            }
        }
        //可以被退的最大产品
        if (isOpenJxc){
            List<OutstockProductEntity> outstockProductEntityList = getRefundRelyBusinessProduct(corpid,contractId,true);
            Iterator<OutstockProductEntity> iterator = outstockProductEntityList.iterator();
            while (iterator.hasNext()){
                OutstockProductEntity entity = iterator.next();
                Double num = entity.getProductNum();
                String key;
                if (needMerge) {
                    key = entity.getProductId().toString();
                } else {
                    key = entity.getProductId() + "_" +entity.getId();
                }
                if (refundMap.containsKey(key)){
                    if (num > refundMap.get(key)){
                        entity.setProductNum(Arith.sub(num, refundMap.get(key)));
                    }else {
                        iterator.remove();
                    }
                }
                if (priceMap.containsKey(key)) {
                    entity.setProductPrice(priceMap.get(key));
                }
                entity.setData(null);
            }
            return outstockProductEntityList;
        }else {
            List<ContractProductEntity> contractProductEntityList = getRefundRelyBusinessProduct(corpid,contractId,false);
            Iterator<ContractProductEntity> iterator = contractProductEntityList.iterator();
            while (iterator.hasNext()){
                ContractProductEntity entity = iterator.next();
                Double num = entity.getProductNum();
                String key;
                if (needMerge) {
                    key = entity.getProductId().toString();
                } else {
                    key = entity.getProductId() + "_" +entity.getId();
                }
                if (refundMap.containsKey(key)){
                    if (num > refundMap.get(key)){
                        entity.setProductNum(Arith.sub(num , refundMap.get(key)));
                    }else {
                        iterator.remove();
                    }
                }
                entity.setData(null);
            }
            return contractProductEntityList;
        }

    }

    /**
     * 查询合同未退货产品
     * @param corpid
     * @param contractId
     * @param isOpenJxc
     * @return
     * @throws XbbException
     */
    public List getRefundRelyBusinessProduct(String corpid, Long contractId, boolean isOpenJxc) throws XbbException {
        if (isOpenJxc){
            return outstockProductModel.getUpStreamProduct(corpid,OutstockTypeEnum.CONTRACT_OUTSTOCK.getCode(),contractId);
        }else {
            Map<String,Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("corpid",corpid);
            param.put("del",0);
            param.put("contractId",contractId);
            param.put("type",ContractProductTypeEnum.CONTNRACT.getCode());
            return contractProductModel.findEntitys(param);
        }
    }

    /**
     * 用户关联产品保存
     * @param corpid
     * @param productList
     * @return
     * @throws XbbException
     */
    public Map<Long,PaasFormDataEntityExt> product2saveMap(String corpid, JSONArray productList) throws XbbException {
        if (productList == null || productList.isEmpty()){
            return new HashMap<>();
        }
        List<Long> productIdLit = new ArrayList<>();
        for (int i = 0; i < productList.size(); i++){
            JSONObject jsonObject = productList.getJSONObject(i);
            productIdLit.add(jsonObject.getLong(SelectProductEnum.PRODUCT.getAttr()));
        }
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword",corpid));
        boolQueryBuilder.filter(termsQuery(FieldTypeEnum.DATAID.getAlias(),productIdLit));
        //移除del等于0条件,该方法用于保存时的数据获取以及部分场景的渲染,保存产品已经经过校验无需二次校验;部分场景需要对已删除产品渲染并展示
//        boolQueryBuilder.filter(termQuery(StringConstant.DEL,DelEnum.NORMAL.getDel()));
        List<String> fieldList = Arrays.asList(FieldTypeEnum.DATAID.getAlias(),ProductEnum.getAttrConnectData(ProductEnum.NAME),ProductEnum.getAttrConnectData(ProductEnum.PRODUCT_NO),ProductEnum.getAttrConnectData(ProductEnum.BARCODE),ProductEnum.getAttrConnectData(ProductEnum.SPECIFICATION_LINK_TEXT),ProductEnum.getAttrConnectData(ProductEnum.UNIT),ProductEnum.getAttrConnectData(ProductEnum.COST),ProductEnum.getAttrConnectData(ProductEnum.PARENT_ID));
        List<PaasFormDataEntityExt> childList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_PRODUCT,boolQueryBuilder,PaasFormDataEntityExt.class,null);
        Map<Long, PaasFormDataEntityExt> productMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (PaasFormDataEntityExt entityExt : childList) {
            JSONObject data = entityExt.getData();
            data.put(ProductEnum.PRODUCT_NO.getAttr(),entityExt.getSerialNo());
            if (Objects.isNull(data.getString(ProductEnum.NAME.getAttr()))){
                data.put(ProductEnum.NAME.getAttr(),"");
            }
            if (Objects.isNull(entityExt.getSerialNo())){
                entityExt.setSerialNo("");
            }
            if (Objects.isNull(data.getString(ProductEnum.UNIT.getAttr()))){
                data.put(ProductEnum.UNIT.getAttr(),"");
            }
            if (Objects.isNull(data.getString(ProductEnum.BARCODE.getAttr()))){
                data.put(ProductEnum.BARCODE.getAttr(),"");
            }
            if (Objects.isNull(data.getDouble(ProductEnum.PRICE.getAttr()))){
                data.put(ProductEnum.PRICE.getAttr(),0D);
            }
            if (Objects.isNull(data.getDouble(ProductEnum.COST.getAttr()))){
                data.put(ProductEnum.COST.getAttr(),0D);
            }
            productMap.put(entityExt.getDataId(),entityExt);
        }
        return productMap;
    }
}
